/////////////////////////////////////////////////////////////////////////////
// Name:        ax_help_provider.cpp
// Purpose:     Special help text provider.
// Author:      Julian Smart
// Modified by:
// Created:     2009-04-16
// RCS-ID:
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"
#include "wx/tooltip.h"
#include "wx/display.h"

#include "ax/ax_utils.h"
#include "ax/ax_help_provider.h"
#include "ax/ax_speech.h"
#include "ax/ax_ui_adaptation.h"

// This class disables tooltips temporarily to avoid having context help on top of a tooltip,
// which looks horrible.

class AxToolTipDisabler
{
public:
    AxToolTipDisabler()
    {
        // ?
        // m_isEnabled = wxGetApp().UsingTooltips();
        m_isEnabled = true;
        if (m_isEnabled)
        {
            wxToolTip::Enable(false);
        }
    }

    ~AxToolTipDisabler()
    {
        if (m_isEnabled)
        {
            wxToolTip::Enable(true);
        }
    }

    bool m_isEnabled;
};

class AxHelpToolTip: public AxHtmlToolTip
{
    DECLARE_CLASS(AxHelpToolTip)
public:
    AxHelpToolTip(AxHelpProvider* provider, wxWindow* window);
    ~AxHelpToolTip();

    virtual wxHtmlWindow* CreateHtmlWindow();

    AxHelpProvider* m_provider;
};

wxWindow* AxHelpProvider::sm_helpWindow = NULL;
wxWindow* AxHelpProvider::sm_invokedFrom = NULL;

AxHelpProvider::AxHelpProvider(AxSpeech* speech)
{
    Init();

    m_speech = speech;
}

AxHelpProvider::~AxHelpProvider()
{
    CleanUp();
}

void AxHelpProvider::Init()
{
    m_speech = NULL;
}

void AxHelpProvider::CleanUp()
{
    if (sm_helpWindow)
    {
        sm_helpWindow->Destroy();
        sm_helpWindow = NULL;
    }
}

wxString AxHelpProvider::GetHelp(const wxWindowBase *window)
{
    wxWindowBase* win = (wxWindowBase*) window;
    if (m_helptextOrigin != wxHelpEvent::Origin_Keyboard && m_helptextAtPoint != wxDefaultPosition)
    {
        wxPoint pt = m_helptextAtPoint;
        m_helptextAtPoint = wxDefaultPosition;
        win = AxUIUtilities::ChildWindowFromPoint(pt, false /* hidden */);
    }

    while (win)
    {
        wxString help = wxSimpleHelpProvider::GetHelp(win);
        
        if (!help.IsEmpty())
            return help;
        
        if (win->IsTopLevel())
            break;
        else
            win = win->GetParent();
    }
    return wxSimpleHelpProvider::GetHelp(window);
}

wxString AxHelpProvider::GetWindowOrAdapterHelp(const wxWindowBase *window)
{
    wxString help;
    AxAdapter* adapter = AxSelfVoicing::FindAdapter((wxWindow*) window);
    if (adapter)
        help = adapter->GetHelpText();
    if (help.IsEmpty())
        help = GetHelp(window);
    return help;
}

bool AxHelpProvider::ShowHelp(wxWindowBase *window)
{
    const wxString text = GetWindowOrAdapterHelp(window);
    if (m_speech && !text.IsEmpty())
    {
        AxSpeechEvent speechEvent;
        speechEvent.SetSpeechEventType(AxSpeechEventText);
        speechEvent.SetVerbosityLevel(AxSpeechVerbosityLow);
        speechEvent.SetText(text);
        
        // Speak immediately in case ShowHelp goes into a loop
        // with no idle processing.
        speechEvent.SetFlags(AxSpeechFlagsPurge|AxSpeechFlagsImmediate);
        m_speech->EmitSpeech(speechEvent);
    }

    if (text.IsEmpty())
        return false;

    AxToolTipDisabler disabler;

#if 1
    wxWindow* parentWindow = (wxWindow*) window;
    if (sm_helpWindow)
    {
        if (parentWindow == sm_helpWindow)
            parentWindow = sm_helpWindow->GetParent();
        else if (parentWindow->GetParent() == sm_helpWindow)
            parentWindow = sm_helpWindow->GetParent()->GetParent();
    }
    
    if (sm_helpWindow)
    {
        sm_helpWindow->Destroy();
        sm_helpWindow = NULL;
    }
    
    AxHelpToolTip* tip = new AxHelpToolTip(this, (wxWindow*) parentWindow);
    
    tip->SetWidth(200);
    tip->SetTip(text);
    tip->SetAutoDestroy(true);
    tip->SetShowDuration(10000);
    
    sm_helpWindow = tip;
    
    //wxPoint pt = wxGetMousePosition() - wxPoint(50, 50);
    wxPoint pt = window->GetPosition() - wxPoint(0, tip->GetSize().y + 10);
    if (window->GetParent())
        pt = window->GetParent()->ClientToScreen(pt);
    
    // Make sure the window doesn't go off the bottom or right of the screen
    int displayIdx = wxDisplay::GetFromWindow(tip);
    
    tip->SetPosition(pt);
    if (displayIdx != wxNOT_FOUND)
    {
        wxDisplay display(displayIdx);
        wxRect clientArea(display.GetClientArea());
        wxRect dialogRect = wxRect(pt, tip->GetSize());
        wxRect originalDialogRect = dialogRect;
        if (dialogRect.GetBottom() > clientArea.GetBottom())
            dialogRect.SetTop(clientArea.GetBottom() - dialogRect.GetHeight());
        if (dialogRect.GetTop() < clientArea.GetTop())
            dialogRect.SetTop(clientArea.GetTop());
        if (dialogRect.GetRight() > clientArea.GetRight())
            dialogRect.SetLeft(clientArea.GetRight() - dialogRect.GetWidth());
        
        if (originalDialogRect != dialogRect)
        {
            pt = dialogRect.GetPosition();
            tip->SetPosition(pt);
        }
    }
    
    tip->StartShowing();
    return true;

#else
    bool success = wxSimpleHelpProvider::ShowHelp(window);
    return success;
#endif
}

#if wxUSE_POPUPWIN
IMPLEMENT_CLASS(AxHtmlToolTip, wxPopupWindow)
#else
IMPLEMENT_CLASS(AxHtmlToolTip, wxFrame)
#endif

#if wxUSE_POPUPWIN
BEGIN_EVENT_TABLE(AxHtmlToolTip, wxPopupWindow)
#else
BEGIN_EVENT_TABLE(AxHtmlToolTip, wxFrame)
#endif
    EVT_CLOSE(AxHtmlToolTip::OnCloseWindow)
    EVT_TIMER(wxID_ANY, AxHtmlToolTip::OnTimeout)
    EVT_CHAR(AxHtmlToolTip::OnChar)
END_EVENT_TABLE()

AxHtmlToolTip::AxHtmlToolTip(wxWindow* window)
{
    Init();

    m_callingWindow = window;

    CreateWindow();

    m_htmlWindow = CreateHtmlWindow();
}

bool AxHtmlToolTip::CreateWindow()
{
    // Must be stay on top of frame, because we set the focus back to the old window
    // and that would hide the tip window.
    // Check that wxFRAME_FLOAT_ON_PARENT works on other platforms.

#if wxUSE_POPUPWIN
    Create(wxGetTopLevelParent(m_callingWindow), wxSIMPLE_BORDER);
#else
    // long windowStyle = wxFRAME_NO_TASKBAR|wxSTAY_ON_TOP;
    long windowStyle = wxFRAME_NO_TASKBAR|wxFRAME_FLOAT_ON_PARENT;
#if defined(__WXMAC__)
    windowStyle |= wxFRAME_TOOL_WINDOW;
#endif
    Create(wxGetTopLevelParent(m_callingWindow), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, windowStyle);
#endif

    return true;
}

void AxHtmlToolTip::Init()
{
    m_autoDestroy = false;
    m_htmlWindow = NULL;
    m_callingWindow = NULL;
    m_htmlWidth = 150;
    m_margin = 10;
    m_showDuration = 5000; // 5 seconds
    m_timer.SetOwner(this);
}

AxHtmlToolTip::~AxHtmlToolTip()
{
    if (m_timer.IsRunning())
        m_timer.Stop();
}

wxHtmlWindow* AxHtmlToolTip::CreateHtmlWindow()
{
    wxHtmlWindow* htmlWindow = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxSize(m_htmlWidth, 100), wxHW_SCROLLBAR_NEVER|wxNO_BORDER);
    htmlWindow->SetBorders(4);
#if defined(__WXGTK20__)
    int fontSize = 11;
    htmlWindow->SetStandardFonts(fontSize);
#endif

    return htmlWindow;
}

void AxHtmlToolTip::OnTimeout(wxTimerEvent& WXUNUSED(event))
{
    if (IsShown())
    {
        Hide();

        if (GetAutoDestroy())
            Destroy();
    }
}

// Use this to start showing the tip
void AxHtmlToolTip::StartShowing()
{
    if (m_timer.IsRunning())
        m_timer.Stop();
    m_timer.Start(m_showDuration, true /* oneShot */);
    Show();
}

void AxHtmlToolTip::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
{
    Destroy();
}

void AxHtmlToolTip::OnChar(wxKeyEvent& event)
{
    if (event.GetKeyCode() == WXK_ESCAPE)
        Destroy();
    else
        event.Skip();
}

void AxHtmlToolTip::SetTip(const wxString& tip)
{
    m_htmlWindow->SetPage(tip);

    m_htmlWindow->SetSize(m_htmlWidth, 100);

    wxSize actualSize = wxSize(m_htmlWindow->GetInternalRepresentation()->GetWidth(), m_htmlWindow->GetInternalRepresentation()->GetHeight());

    if (actualSize.x <= m_htmlWidth)
        actualSize.x = m_htmlWindow->GetSize().x;
    actualSize.y += 5;

    m_htmlWindow->SetSize(actualSize);
    m_htmlWindow->SetInitialSize(actualSize);
    SetClientSize(m_htmlWindow->GetSize());
}

// Move the tip to a suitable position relative to the object whose size
// is defined by rect
void AxHtmlToolTip::MoveToGoodPosition(const wxRect& rect)
{
    // Make sure the window doesn't go off the bottom or right of the screen
    int displayIdx = wxDisplay::GetFromWindow(m_callingWindow);
    wxDisplay display(displayIdx);

    wxRect currentRect = GetRect();

    wxPoint windowPosition;
    if (m_callingWindow)
        windowPosition = m_callingWindow->GetParent()->ClientToScreen(m_callingWindow->GetPosition());
    else
        windowPosition = wxPoint(0, 0);

    wxRect clientArea;
    if (m_callingWindow)
        clientArea = wxRect(windowPosition, m_callingWindow->GetSize());
    else
        clientArea = display.GetClientArea();

    if ((rect.GetRight() + currentRect.GetWidth() + m_margin) > clientArea.GetRight())
    {
        // Show to the left of the object
        currentRect.SetPosition(wxPoint(rect.GetX() - currentRect.GetWidth() - m_margin, rect.GetY()));
    }
    else
    {
        // Show to the right of the object
        currentRect.SetPosition(wxPoint(rect.GetRight() + m_margin, rect.GetY()));
    }

    // Now make sure it doesn't go off the display

    if (currentRect.GetBottom() > clientArea.GetBottom())
        currentRect.SetTop(clientArea.GetBottom() - currentRect.GetHeight());
    if (currentRect.GetRight() > clientArea.GetRight())
        currentRect.SetLeft(clientArea.GetRight() - currentRect.GetWidth());
    
    SetPosition(currentRect.GetPosition());
}

IMPLEMENT_CLASS(AxHelpToolTip, AxHtmlToolTip)

AxHelpToolTip::AxHelpToolTip(AxHelpProvider* provider, wxWindow* window)
{
    m_provider = provider;
    SetCallingWindow(window);
    CreateWindow();
    SetHtmlWindow(CreateHtmlWindow());
}

AxHelpToolTip::~AxHelpToolTip()
{
    AxHelpProvider::sm_helpWindow = NULL;
    if (m_provider)
        m_provider->SetHelpWindow(NULL);
}

wxHtmlWindow* AxHelpToolTip::CreateHtmlWindow()
{
    //wxHtmlWindow* htmlWindow = new wcHelpHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxSize(m_htmlWidth, 100), wxHW_SCROLLBAR_NEVER|wxNO_BORDER);
    wxHtmlWindow* htmlWindow = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxSize(m_htmlWidth, 100), wxHW_SCROLLBAR_NEVER|wxNO_BORDER);
    htmlWindow->SetBorders(4);

    return htmlWindow;
}
